<template>
  <div>
    <div id="phaser"></div>
    <div id="blocklyDiv" style="display: inline-block; height: 600px; width: 600px"></div>
    <el-button type="primary" @click="start()">执行代码</el-button>
    <el-button type="info" @click="reset()">重置</el-button>
  </div>
</template>
<script>
import "pixi";
import "p2";
import Phaser from "phaser";

import Blockly from "scratch-blocks";
import Interpreter from "js-interpreter";
import jsInit from "../lib/javascript_compressed";

export default {
  name: "maze",
  data() {
    return {
      imgUrls: {
        pool: require("../assets/images/background.png"),
        exit: require("../assets/images/exit.png"),
        obstacle: require("../assets/images/obstacle.png"),
        road: require("../assets/images/road.png"),
        start: require("../assets/images/start.png")
      },
      initMap: null,
      moveOrder: [],
      orderIndex: 0,
      run: false
    };
  },
  methods: {
    start() {
      this.run = true;
    },
    reset() {
      this.run = false;
    }
  },
  created() {},
  mounted() {
    // 读取地图
    this.$http
      .get("../../static/init_map.json")
      .then(res => {
        this.initMap = JSON.parse(JSON.stringify(res.data));
      })
      .then(() => {
        // Blockly
        Blockly.JavaScript = jsInit(Blockly);
        // console.log(Blockly.JavaScript);
        Blockly.Blocks["controls_repeat_ext"] = {
          init: function() {
            this.jsonInit({
              message0: "%1 %2 %3",
              args0: [
                {
                  type: "input_value",
                  name: "TIMES",
                  check: "Number"
                },
                {
                  type: "field_image",
                  src:
                    Blockly.mainWorkspace.options.pathToMedia +
                    "icons/control_repeat.svg",
                  width: 40,
                  height: 40,
                  alt: "*",
                  flip_rtl: true
                },
                {
                  type: "input_statement",
                  name: "DO"
                }
              ],
              previousStatement: null,
              nextStatement: null,
              colour: "320"
            });
          }
        };
        Blockly.JavaScript["controls_repeat_ext"] = function(a) {
          var b = a.getField("TIMES")
              ? String(Number(a.getFieldValue("TIMES")))
              : Blockly.JavaScript.valueToCode(
                  a,
                  "TIMES",
                  Blockly.JavaScript.ORDER_ASSIGNMENT
                ) || "0",
            c = Blockly.JavaScript.statementToCode(a, "DO");
          c = Blockly.JavaScript.addLoopTrap(c, a.id);
          a = "";
          var d = Blockly.JavaScript.variableDB_.getDistinctName(
              "count",
              Blockly.Variables.NAME_TYPE
            ),
            e = b;
          b.match(/^\w+$/) ||
            Blockly.isNumber(b) ||
            ((e = Blockly.JavaScript.variableDB_.getDistinctName(
              "repeat_end",
              Blockly.Variables.NAME_TYPE
            )),
            (a += "var " + e + " = " + b + ";\n"));
          return (
            a +
            ("for (var " +
              d +
              " = 0; " +
              d +
              " < " +
              e +
              "; " +
              d +
              "++) {\n" +
              c +
              "}\n")
          );
        };
        Blockly.Blocks["moveEast"] = {
          init: function() {
            this.jsonInit({
              message0: "moveEast",
              previousStatement: null,
              nextStatement: null,
              colour: 230,
              tooltip: "hello"
            });
          }
        };

        Blockly.JavaScript["moveEast"] = function(block) {
          return "moveEast();\n";
        };

        Blockly.JavaScript["moveWest"] = function(block) {
          return "moveWest();\n";
        };
        Blockly.Blocks["moveWest"] = {
          init: function() {
            this.jsonInit({
              message0: "moveWest",
              previousStatement: null,
              nextStatement: null,
              colour: 150,
              tooltip: "hello"
            });
          }
        };

        Blockly.JavaScript["moveNorth"] = function(block) {
          return "moveNorth();\n";
        };
        Blockly.Blocks["moveNorth"] = {
          init: function() {
            this.jsonInit({
              message0: "moveNorth",
              previousStatement: null,
              nextStatement: null,
              colour: 350,
              tooltip: "hello"
            });
          }
        };

        Blockly.JavaScript["moveSouth"] = function(block) {
          return "moveSouth();\n";
        };
        Blockly.Blocks["moveSouth"] = {
          init: function() {
            this.jsonInit({
              message0: "moveSouth",
              previousStatement: null,
              nextStatement: null,
              colour: 128,
              tooltip: "hello"
            });
          }
        };

        Blockly.JavaScript["eatNut"] = function(block) {
          return "eatNut();\n";
        };
        Blockly.Blocks["eatNut"] = {
          init: function() {
            this.jsonInit({
              message0: "eatNut",
              previousStatement: null,
              nextStatement: null,
              colour: 128,
              tooltip: "hello"
            });
          }
        };
        let toolboxBlocks = `<xml>
                              <block type="controls_repeat_ext">
                                <value name="TIMES">
                                    <shadow type="math_number">
                                    <field name="NUM">4</field>
                                    </shadow>
                                </value>
                              </block>    
                          <block type="moveEast"></block>
                          <block type="moveWest"></block>
                          <block type="moveNorth"></block>
                          <block type="moveSouth"></block>
                          <block type="eatNut"></block>
                             </xml>`;
        let startBlocks = `<xml><block type="moveEast"></block></xml>`;
        let defaultXml = Blockly.Xml.textToDom(startBlocks);
        let workspace = Blockly.inject("blocklyDiv", {
          toolbox: toolboxBlocks,
          media: "./static/media/"
        });
        Blockly.Xml.domToWorkspace(defaultXml, workspace);

        // JS interpreter
        let interpreter = null;
        function initApi(interpreter, scope) {
          // Add moveEast
          var wrapper = function() {
            return interpreter.createPrimitive(addMove(0));
          };
          interpreter.setProperty(
            scope,
            "moveEast",
            interpreter.createNativeFunction(wrapper)
          );

          // Add moveWest
          var wrapper = function() {
            return interpreter.createPrimitive(addMove(180));
          };
          interpreter.setProperty(
            scope,
            "moveWest",
            interpreter.createNativeFunction(wrapper)
          );

          // Add moveNorth
          var wrapper = function() {
            return interpreter.createPrimitive(addMove(270));
          };
          interpreter.setProperty(
            scope,
            "moveNorth",
            interpreter.createNativeFunction(wrapper)
          );

          // Add moveSouth
          var wrapper = function() {
            return interpreter.createPrimitive(addMove(90));
          };
          interpreter.setProperty(
            scope,
            "moveSouth",
            interpreter.createNativeFunction(wrapper)
          );

          // Add nut
          var wrapper = function() {
            return interpreter.createPrimitive(addMove(100));
          };
          interpreter.setProperty(
            scope,
            "eatNut",
            interpreter.createNativeFunction(wrapper)
          );
        }

        // phaser
        let game = new Phaser.Game(600, 600, Phaser.AUTO, "phaser");
        game.States = {};
        const self = this;
        let player = null;
        let exit = null;
        let obstacles = null;
        let isSucceeded = false;
        let isPeng = false;

        // state preview
        game.States.preview = function() {
          this.preload = function() {
            game.load.image("pool", self.imgUrls.pool);
            game.load.image("obstacle", self.imgUrls.obstacle);
            game.load.image("exit", self.imgUrls.exit);
            game.load.image("road", self.imgUrls.road);
            game.load.image("start", self.imgUrls.start);
          };
          this.create = function() {
            game.physics.startSystem(Phaser.Physics.ARCADE);
            game.add.tileSprite(0, 0, game.width, game.height, "pool");
            self.initMap.map(item => {
              game.add.sprite(item.x, item.y, item.type);
            });
          };
          this.update = function() {
            if (self.run) {
              runCode();
              game.state.start("start");
            }
          };
        };

        // state start
        game.States.start = function() {
          this.preload = function() {
            game.load.image("pool", self.imgUrls.pool);
            game.load.image("obstacle", self.imgUrls.obstacle);
            game.load.image("exit", self.imgUrls.exit);
            game.load.image("road", self.imgUrls.road);
            game.load.image("start", self.imgUrls.start);
          };
          this.create = function() {
            isSucceeded = false;
            isPeng = false;
            game.physics.startSystem(Phaser.Physics.ARCADE);
            game.add.tileSprite(0, 0, game.width, game.height, "pool");
            obstacles = game.add.physicsGroup(Phaser.Physics.ARCADE);
            self.initMap.map(item => {
              if (item.type === "start") {
                player = game.add.sprite(item.x, item.y, item.type);
                game.physics.arcade.enable(player);
                player.body.collideWorldBounds = true;
                player.body.onMoveComplete.add(nextMove, this);
                player.body.onWorldBounds = new Phaser.Signal();
                player.body.onWorldBounds.add(peng, this);
              } else if (item.type === "exit") {
                exit = game.add.sprite(item.x, item.y, item.type);
                game.physics.arcade.enable(exit);
              } else if (item.type === "obstacle") {
                obstacles.create(item.x, item.y, item.type);
              }
            });
            nextMove();
          };
          this.update = function() {
            if (!self.run) {
              self.moveOrder = [];
              self.orderIndex = 0;
              game.state.start("preview");
            }
            game.physics.arcade.overlap(player, exit, success, getIn);
            game.physics.arcade.overlap(player, obstacles, peng, getIn);
          };
        };
        game.state.add("preview", game.States.preview);
        game.state.add("start", game.States.start);
        game.state.start("preview");

        function runCode() {
          let code = Blockly.JavaScript.workspaceToCode(workspace);
          interpreter = new Interpreter(code, initApi);
          interpreter.run();
        }

        function getIn(obj1, obj2) {
          if (
            Math.abs(obj1.position.x - obj2.position.x) < 30 &&
            Math.abs(obj1.position.y - obj2.position.y) < 30
          ) {
            return true;
          } else {
            return false;
          }
          return false;
        }

        function success() {
          if (!isSucceeded) console.log("mission complete");
          isSucceeded = true;
        }
        function peng() {
          if (!isPeng) console.log("peng");
          isPeng = true;
        }
        function nextMove() {
          console.log(self.orderIndex, self.moveOrder.length);
          if (self.orderIndex > self.moveOrder.length - 1) {
            return;
          }
          // 特殊操作，如吃松果。
          if (self.moveOrder[self.orderIndex] === 100) {
            setTimeout(() => {
              console.log("eat nut");
              self.orderIndex++;
              player.body.onMoveComplete.dispatch();
            }, 1000);
            return;
          }
          player.body.moveFrom(1000, 150, self.moveOrder[self.orderIndex++]);
        }
        function addMove(direction) {
          // console.log(self.moveOrder);
          self.moveOrder.push(direction);
        }
      });
  }
};
</script>
<style scoped>
#phaser {
  display: inline-block;
}
</style>
